// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright © 2018-2019 Ariadne Devos
/* shttpd - functions for workers */

/* # High-level architecture

   There are multiple processes.
   In a process, there are multiple workers.
   Each worker has its own file descriptor table.
   This table can have the following format:
     0: unix socket (control lifecycle)
     1: /dev/null (for now)
     2: /dev/null (for now)
     3: epollfd
   ???: asynchronous IO?
   ...: accept sockets, read/write sockets, open files,
        connections to external services ...

   The unix socket is for controlling when the worker should stop.
   The worker polls for changes in file descriptor readyness and reacts
   to those and asynchronous IO events.

   Everything is mutually distrusting.

   # Seperation of knowledge

   Use case: a HTTPS server. A HTTPS server conceptually is TLS encryption
   after a HTTP server encrypted connections. The HTTP code and TLS code
   (or even HTTPS code and encryption and signing) can live in different
   workers or processes.

   TODO: an intra-shttpd communication system (passing around and transcribing
   paper).

   # Paper

   A character buffer that can be passed around.

   # The control thread

   - Control thread creates worker.
   - Control thread -> asks worker to perform apoptosis
   - Control thread -> sends tasks to worker
   - Control thread -> asks tasks from worker -> sends tasks to control thread
   - Worker thread -> reports hard errors to control thread
   - Kernel -> notifies worker -> asks kernel to do stuff
 */

#include <stddef.h>
#include <stdint.h>
#include <sys/epoll.h>

/* Don't change this group! control.c relies upon these values. */
#define sHT_FD_CONTROL 0
#define sHT_FD_STDOUT 1
#define sHT_FD_STDERR 2

/* Only for sHT_start_worker. Points to a worker executable. */
#define sHT_FD_WORKEREXE 4

#include <sHT/paper.h>
#include <sHT/task.h>
#include <sHT/taskset.h>
#include <sHT/scheduling.h>


typedef unsigned int sHT_worker_flags;
#define sHT_WORKER_OOM 1
#define sHT_WORKER_LIMIT_TASKS 2
#define sHT_WORKER_LIMIT_PAPER 4
#define sHT_WORKER_LIMIT_WATCHES 8
/* file descriptor */
#define sHT_WORKER_LIMIT_FDS 16
/* file descriptor */
#define sHT_WORKER_SYSTEM_FDS 32

struct sHT_objcache;

struct sHT_worker
{
	struct sHT_objcache *papers;
	/* Use sHT_deschedule_rr, sHT_schedule_rr */
	struct sHT_taskset todo;
	struct sHT_objcache *free_streams;
	/* length: todo->n */
	struct epoll_event *epoll_events;
	/* indexed by fd, length: todo->capacity */
	struct sHT_task *tasks;
	/* The control thread is notified if any is set. */
	sHT_worker_flags flags;
	sHT_watch_set watches;
};

/* epoll_wait(2) returned n events. Schedule corresponding tasks, setting
   task->epollflags.

   @var{n} must be less than SSIZE_MAX. */
__attribute__((nonnull (1)))
void
sHT_schedule_events(struct sHT_worker *worker, size_t n);

/* _logic$ is defined by worker-specific code */
__attribute__((nonnull (1, 2)))
void
sHT_diy_logic(struct sHT_worker *worker, struct sHT_task *task);

__attribute__((nonnull (1, 2)))
void
sHT_aio_logic(struct sHT_worker *worker, struct sHT_task *task);

/* Send information about resource utilisation to the control thread.
   More specifically,
   send and clear task->flags.
   Doesn't allocate any resources.
   (May be extended) */
__attribute__((nonnull (1, 2)))
void
sHT_sendstatus_task(struct sHT_worker *worker, struct sHT_task *task);

/* Perform the IO actions of worker->io_type.
   Then, call sHT_diy_logic, sHT_socket_logic, sHT_aio_logic or
   sHT_accept_logic, depending on the io type.

   task must reschedule itself if it:
   - it has some things to do, even if no IO is possible
 */
__attribute__((nonnull (1, 2)))
void
sHT_perform_task(struct sHT_worker *worker, struct sHT_task *task);

__attribute__((nonnull (1)))
_Noreturn void
sHT_worker(struct sHT_worker *worker);
